<template>
  <div class="coupon-report">
    <div class="card-wrap">
      <div class="card-box" v-for="(item, index) in cardList" :key="index">
        <div class="card-top">
          <span class="title">{{ item.title }}</span>
          <el-tooltip popper-class="desc-tooltip">
            <i class="el-icon-warning-outline"></i>
            <template #content>
              <div class="desc" v-html="item.describe"> </div>
            </template>
          </el-tooltip>
        </div>
        <h5 class="count">{{ fmtNumer(item.count, item.title) }}</h5>
        <div class="more">
          <div class="more-item" v-for="(d, i) in item.more" :key="i">
            <span>{{ d.label }}</span>
            <span class="num">
              {{ d.prefix }}
              {{ fmtNumer(d.count, d.percent) }}</span>
          </div>
        </div>
      </div>
    </div>
    <div class="chart-wrap">
      <div class="condition">
        <div class="filters">
          <span class="filter">
            <span class="label">渠道</span>
            <el-cascader v-model="queryParams.channel_id" :options="channelOpts"
              :props="{ multiple: true, emitPath: false }" style="width: 260px" collapse-tags :show-all-levels="false"
              clearable filterable @change="renderChart"></el-cascader>
          </span>
          <span class="filter">
            <span class="label">选择时间</span>
            <el-date-picker size="small" v-model="queryParams.time_range" type="daterange" placeholder="选择日期"
              :clearable="false" value-format="yyyy-MM-dd" :picker-options="pickerOptions" @change="renderChart">
            </el-date-picker>
          </span>
        </div>
        <div class="btns">
          <el-button
            size="small"
            type="success"
            :loading="exportLoading"
            @click="exportExcel">导出</el-button>
          <el-button type="primary" size="small" @click="renderChart">查询</el-button>
          <el-button type="info" size="small" @click="reset">重置</el-button>
        </div>
      </div>
      <div class="content">
        <div class="line-chart">
          <e-charts v-loading="loading1" class="chart" :options="option1" autoresize />
          <e-charts v-loading="loading2" class="chart" :options="option2" autoresize />
          <e-charts v-loading="loading3" class="chart" :options="option3" autoresize />
          <e-charts v-loading="loading4" class="chart" :options="option4" autoresize />
        </div>
        <e-charts v-loading="loading5" class="area-chart" :options="option5" autoresize />
      </div>
    </div>
  </div>
</template>

<script type="text/javascript">
import { mapGetters } from 'vuex'
import { getReportData, queryExportCouponTask, createExportCouponTask } from '@/api/coupon'
import { getChannelByAppId } from '@/api/system/channel'
import { parseTime, toFixed, numToText, downloadExportUrl } from '@/utils/index'

import ECharts from '@/components/Echarts'
import * as echarts from 'echarts/core'
import { LineChart } from 'echarts/charts'
import { DataZoomComponent } from 'echarts/components'
import Axios from 'axios'
import { getDashboardIds, getDataSources } from '@/api/feature/index'
echarts.use([LineChart, DataZoomComponent])
const dateFmt = '{y}-{m}-{d}'
const d = new Date()
d.setDate(d.getDate() - 1)
const end = parseTime(d, dateFmt)
const start = parseTime(d.setDate(d.getDate() - 13), dateFmt)
export default {
  name: 'CouponReport',
  components: {
    ECharts
  },
  data () {
    return {
      queryParams: {
        channel_id: [],
        time_range: [start, end],
        datasource: 13
      },
      cardList: [{
        title: '总成交',
        describe: '日均销售额：本优惠券总成交额/天数<br/>每笔单价：本优惠券总成交额/订单数',
        count: 0,
        more: [{
          prefix: '￥',
          label: '日均销售额',
          count: 0
        },
        {
          prefix: '￥',
          label: '每笔单价',
          count: 0
        }]
      },
      {
        title: '总抵扣',
        describe: '日均抵扣额：总抵扣/天数<br/>每张抵扣：总抵扣额/券的使用量',
        count: 0,
        more: [{
          prefix: '￥',
          label: '日均抵扣额',
          count: 0
        },
        {
          prefix: '￥',
          label: '每张抵扣',
          count: 0
        }]
      },
      {
        title: '总领取数',
        describe: '领取率=领取数量/发放数量',
        count: 0,
        more: [{
          label: '领取率',
          count: 0,
          percent: true
        }]
      },
      {
        title: '总使用数',
        describe: '使用率=使用数量/领取数量<br/>用券订单比例=<br/>使用优惠券订单量/总订单量',
        count: 0,
        more: [{
          label: '使用率',
          count: 0,
          percent: true
        },
        {
          label: '用券的订单比率',
          count: 0,
          percent: true
        }]
      }],
      pickerOptions: {
        disabledDate (time) {
          return time.getTime() >= new Date().setHours(0, 0, 0, 0)
        },
        shortcuts: [{
          text: '过去3日',
          onClick (picker) {
            const end = new Date()
            const start = new Date()
            start.setDate(start.getDate() - 3)
            end.setDate(end.getDate() - 1)
            picker.$emit('pick', [start, end])
          }
        }, {
          text: '过去8日',
          onClick (picker) {
            const end = new Date()
            const start = new Date()
            start.setDate(start.getDate() - 8)
            end.setDate(end.getDate() - 1)
            picker.$emit('pick', [start, end])
          }
        }, {
          text: '过去15日',
          onClick (picker) {
            const end = new Date()
            const start = new Date()
            start.setDate(start.getDate() - 15)
            end.setDate(end.getDate() - 1)
            picker.$emit('pick', [start, end])
          }
        }, {
          text: '过去30日',
          onClick (picker) {
            const end = new Date()
            const start = new Date()
            start.setDate(start.getDate() - 30)
            end.setDate(end.getDate() - 1)
            picker.$emit('pick', [start, end])
          }
        }]
      },
      channelOpts: [],
      exportLoading: false,
      loading1: false,
      loading2: false,
      loading3: false,
      loading4: false,
      loading5: false,
      option1: null,
      option2: null,
      option3: null,
      option4: null,
      option5: null
    }
  },
  created () {
    this.getChannels()
    const list = [
      { name: 'coupon_order' },
      { name: 'coupon_detail' },
      { name: 'coupon_show' },
      { name: 'coupon_unsed' }
    ]
    getDashboardIds({ merchant_id: this.$store.getters['zcsd-core/checkProject'].merchant_id, page: list.map(item => item.name).join() }).then(res => {
      const data = (res.data || []).map(item => {
        const obj = list.find(it => it.name === item.page) || {}
        obj.dashboard_id = item.dashboard_id
        return item.dashboard_id
      })
      getDataSources({ dashboard_id: data.join() }).then(res => {
        (res.data || []).map(item => {
          const obj = list.find(it => it.dashboard_id === item.dashboard_id) || {}
          obj.data_source = item.data_source
        })
        this.queryParams.datasource = list[1].data_source
        this.dataSourceList = list
        console.log(list)
        this.init()
      })
    })
  },
  methods: {
    init () {
      this.getAmount()
      this.renderChart()
    },
    /** 点击-全部导出 */
    exportExcel () {
      this.exportLoading = true
      const params = {
        draw: this.drawParams,
        use: this.useParams,
        amount: this.amountParams,
        discount: this.discountParams
      }
      createExportCouponTask({ filter: params })
        .then(response => {
          const { task_id } = response.data || {}
          if (task_id) {
            this.queryExportTask(task_id, () => {
              this.exportLoading = false
            })
          } else {
            this.$message.error('导出订单任务创建失败')
            this.exportLoading = false
          }
        })
        .catch(e => {
          this.exportLoading = false
        })
    },
    /** 查询导出订单任务结果 */
    queryExportTask (task_id, func) {
      queryExportCouponTask({ task_id })
        .then(response => {
          if (response.data?.status === 'finished') {
            downloadExportUrl(response.data.file_url)
            typeof func === 'function' && func(true)
          } else {
            setTimeout(() => {
              this.queryExportTask(task_id, func)
            }, 1000)
          }
        })
        .catch(() => {
          typeof func === 'function' && func(false)
        })
    },
    // eslint-disable-next-line sonarjs/cognitive-complexity
    getAmount () {
      const { st, et } = this.$route.query
      this.queryParams.coupon_id = this.$route.params.coupon_id
      const days = (st && et) ? Math.ceil((et - st) / 3600 / 24) : 1
      const time_range = [parseTime(st, dateFmt), parseTime(et, dateFmt)]
      const datasource = this.dataSourceList[0].data_source
      Axios.all([this.getChartData({
        ...this.queryParams,
        status: 2,
        indicator: '1,2,3', // 1-优惠券交易总额,2-抵扣金额,3-订单量
        datasource,
        time_range
      }),
      this.getChartData({
        ...this.queryParams,
        indicator: 3, // 优惠券领取总量
        time_range
      }),
      this.getChartData({
        ...this.queryParams,
        indicator: 1, // 优惠券发放量
        datasource: this.dataSourceList[2].data_source,
        time_range
      }),
      this.getChartData({
        ...this.queryParams,
        status: 2,
        indicator: 2, // 优惠券使用总量（张）
        time_range
      }),
      this.getChartData({
        ...this.queryParams,
        status: 2,
        indicator: 3, // 订单总量
        datasource,
        coupon_id: '',
        time_range
      })]).then(Axios.spread((res1, res2, res3, res4, res5) => {
        const statObj1 = ((Array.isArray(res1.data[0].result) && res1.data[0].result[0].data) || [])[0]
        const statObj2 = ((Array.isArray(res2.data[0].result) && res2.data[0].result[0].data) || [])[0]
        const statObj3 = ((Array.isArray(res3.data[0].result) && res3.data[0].result[0].data) || [])[0]
        const statObj4 = ((Array.isArray(res4.data[0].result) && res4.data[0].result[0].data) || [])[0]
        const statObj5 = ((Array.isArray(res5.data[0].result) && res5.data[0].result[0].data) || [])[0]
        if (statObj1) {
          // 交易总额
          const tradTotal = (statObj1['优惠券交易总额'] || 0) / 100
          const orderCount = statObj1['订单总量'] || 0
          this.cardList[0].count = tradTotal
          // 日均销售额：本优惠券总成交额/天数
          this.cardList[0].more[0].count = this.fmtNumer(this.formatNumber(tradTotal / days, false, 2))
          // 每笔单价：本优惠券总成交额/订单数
          this.cardList[0].more[1].count = this.fmtNumer(this.formatNumber(tradTotal / orderCount, false, 2))
          const disTotal = (statObj1['折扣金额'] || 0) / 100
          this.cardList[1].count = disTotal
          // 日均抵扣额：总抵扣/天数
          this.cardList[1].more[0].count = this.fmtNumer(this.formatNumber(disTotal / days, false, 2))
          if (statObj5) {
            const count = statObj5['订单总量'] || 0
            // 用券订单比例=使用优惠券订单量/总订单量
            this.cardList[3].more[1].count = this.fmtNumer(this.formatNumber(statObj1['订单总量'] / count, true))
          }
        }
        if (statObj2) {
          this.cardList[2].count = statObj2['优惠券领取总量'] || 0
        }
        if (statObj3) {
          const count = statObj3['优惠券展示量'] || 0
          // 领取率=领取数量/发放数量
          this.cardList[2].more[0].count = this.fmtNumer(this.formatNumber(this.cardList[2].count / count, true))
        }
        if (statObj4) {
          const count = statObj4['优惠券使用总量'] || 0
          // 每张抵扣：总抵扣额/券的使用量
          this.cardList[1].more[1].count = this.fmtNumer(this.formatNumber(this.cardList[1].count / count, false, 2))
          this.cardList[3].count = count || 0
          // 使用率=使用数量/领取数量
          this.cardList[3].more[0].count = this.fmtNumer(this.formatNumber(count / this.cardList[2].count, true))
        }
      }))
    },
    async renderChart () {
      // 领取量
      const datasource = this.dataSourceList[0].data_source
      this.loading1 = true
      this.drawParams = this.getParameter({
        ...this.queryParams,
        time_grain_sqla: 'P1D',
        indicator: [3],
        group_by: 'channel_id',
        granularity: 'created_at'
      })
      this.useParams = this.getParameter({
        ...this.queryParams,
        time_grain_sqla: 'P1D',
        indicator: [2],
        status: [2, 3],
        group_by: 'channel_id',
        granularity: 'created_at'
      })
      this.amountParams = this.getParameter({
        ...this.queryParams,
        time_grain_sqla: 'P1D',
        datasource,
        status: 2,
        indicator: [1],
        group_by: 'channel_id',
        granularity: 'created_at'
      })
      this.discountParams = this.getParameter({
        ...this.queryParams,
        time_grain_sqla: 'P1D',
        datasource,
        status: 2,
        indicator: [2],
        group_by: 'channel_id',
        granularity: 'created_at'
      })
      getReportData(this.drawParams).then(res => {
        const list = (Array.isArray(res.data[0].result) && res.data[0].result[0].data) || []
        this.option1 = this.renderLine(list, '优惠券领取量统计')
      }).catch(() => { }).finally(() => (this.loading1 = false))
      // 使用量
      this.loading2 = true
      getReportData(this.useParams).then(res => {
        const list = (Array.isArray(res.data[0].result) && res.data[0].result[0].data) || []
        this.option2 = this.renderLine(list, '优惠券使用量统计')
      }).catch(() => { }).finally(() => (this.loading2 = false))
      // 成交金额
      this.loading3 = true
      getReportData(this.amountParams).then(res => {
        const list = (Array.isArray(res.data[0].result) && res.data[0].result[0].data) || []
        this.option3 = this.renderLine(list, '优惠券成交总额', 100)
      }).catch(() => { }).finally(() => (this.loading3 = false))
      // 抵扣金额
      this.loading4 = true
      getReportData(this.discountParams).then(res => {
        const list = (Array.isArray(res.data[0].result) && res.data[0].result[0].data) || []
        this.option4 = this.renderLine(list, '优惠券抵扣总额', 100)
      }).catch(() => { }).finally(() => (this.loading4 = false))
      this.renderArea()
    },
    renderLine (list, title, scale = 1) {
      const listMap = this.translateToMap(list)
      const xData = []
      const { time_range } = this.queryParams
      const st = new Date(time_range[0])
      st.setHours(8, 0, 0, 0)
      const et = new Date(time_range[1]).setHours(8, 0, 0, 0)
      const channels = list.reduce((arr, it) => {
        for (let k in it) {
          if (k !== '__timestamp' && !arr.includes(k)) arr.push(k)
        }
        return arr
      }, [])
      const yData = channels.map(value => ({
        value,
        name: `(${value})`,
        type: 'line',
        data: []
      }))
      while (Date.parse(st) <= et) {
        const obj = listMap[Date.parse(st)]
        xData.push(
          `${parseTime(st, dateFmt)}`
        )
        yData.forEach(it => {
          it.data.push({
            value: ((obj && obj[it.value]) || 0) / scale
          })
        })
        st.setDate(st.getDate() + 1)
      }
      return {
        title: {
          text: title
        },
        tooltip: {
          trigger: 'axis'
        },
        legend: {
          type: 'scroll',
          left: 20,
          right: 20,
          bottom: 10
        },
        grid: {
          left: '5%'
        },
        xAxis: {
          type: 'category',
          boundaryGap: false,
          data: xData,
          axisLabel: {
            formatter: function (value) {
              return value.slice(-5).replace(/-/, '.')
            }
          }
        },
        yAxis: {
          type: 'value'
        },
        series: yData
      }
    },
    renderArea () {
      this.loading5 = true
      // 已使用的数量/已过期总量/
      Axios.all([this.getChartData({
        ...this.queryParams,
        time_grain_sqla: 'P1D',
        indicator: 1,
        datasource: this.dataSourceList[3].data_source,
        granularity: 'day' // 待使用
      }), this.getChartData({
        ...this.queryParams,
        time_grain_sqla: 'P1D',
        status: [1, 2],
        indicator: 2,
        granularity: 'used_at' // 已使用
      }), this.getChartData({
        ...this.queryParams,
        time_grain_sqla: 'P1D',
        status: 0,
        indicator: 1,
        granularity: 'expire_at' // 已过期
      // eslint-disable-next-line sonarjs/cognitive-complexity
      })]).then(Axios.spread((res1, res2, res3) => {
        const list1 = (Array.isArray(res1.data[0].result) && res1.data[0].result[0].data) || []
        const list2 = (Array.isArray(res2.data[0].result) && res2.data[0].result[0].data) || []
        const list3 = (Array.isArray(res3.data[0].result) && res3.data[0].result[0].data) || []
        const listMap = {}
        const keyMap = {
          '优惠券待使用': '待使用',
          '优惠券使用总量': '已使用',
          '优惠券过期总量': '已过期'
        };
        [...list1, ...list2, ...list3].forEach(item => {
          if (!listMap[item.__timestamp]) listMap[item.__timestamp] = {}
          for (let key in item) {
            if (key !== '__timestamp') {
              const k = keyMap[key] || key
              listMap[item.__timestamp][k] = item[key]
            }
          }
        })
        const xData = []
        const yData = [{
          name: '待使用',
          type: 'line',
          stack: 'Total',
          areaStyle: {},
          showSymbol: false,
          emphasis: {
            focus: 'series'
          },
          data: []
        },
        {
          name: '已使用',
          type: 'line',
          stack: 'Total',
          areaStyle: {},
          showSymbol: false,
          emphasis: {
            focus: 'series'
          },
          data: []
        },
        {
          name: '已过期',
          type: 'line',
          stack: 'Total',
          areaStyle: {},
          showSymbol: false,
          emphasis: {
            focus: 'series'
          },
          data: []
        }]
        const { time_range } = this.queryParams
        const st = new Date(time_range[0])
        st.setHours(8, 0, 0, 0)
        const et = new Date(time_range[1]).setHours(8, 0, 0, 0)
        while (Date.parse(st) <= et) {
          const obj = listMap[Date.parse(st)]
          xData.push(
            `${parseTime(st, dateFmt)}`
          )
          const total = yData.reduce((s, t) => {
            s += (obj && obj[t.name]) || 0
            return s
          }, 0)
          yData.forEach(it => {
            const value = (obj && obj[it.name]) || 0
            it.data.push({
              name: it.name,
              value,
              percent: this.formatNumber(value / total, true)
            })
          })
          st.setDate(st.getDate() + 1)
        }
        const option = {
          tooltip: {
            trigger: 'axis',
            axisPointer: {
              type: 'cross',
              label: {
                backgroundColor: '#6a7985'
              }
            },
            formatter: (params) => {
              let str = `
              <div class="tooltip-wrap">
                <div class="title">${params[0].axisValueLabel}</div>
            `
              params.forEach(it => {
                str += `<div class="series">
                        <span class="mark" style="background-color:${it.color};"></span>
                        <span class="label">${it.seriesName}</span>
                        <span class="value">${this.fmtNumer(it.value)}&nbsp;&nbsp;${it.data.percent}%</span>
                      </div>`
              })
              str += '</div>'
              return str
            }
          },
          legend: {
            show: true
          },
          grid: {
            left: '5%'
          },
          xAxis: [
            {
              type: 'category',
              boundaryGap: false,
              data: xData,
              axisLabel: {
                formatter: function (value) {
                  return value.slice(-5).replace(/-/, '.')
                }
              }
            }
          ],
          yAxis: [
            {
              type: 'value'
            }
          ],
          series: yData
        }
        const maxVal = 31
        if (xData.length > maxVal) {
          option.dataZoom = [
            {
              type: 'inside',
              start: 0,
              end: maxVal / xData.length * 100
            },
            {
              start: 0,
              end: 10
            }
          ]
        }
        this.option5 = option
      })).finally(() => {
        this.loading5 = false
      })
    },
    reset () {
      this.queryParams.channel_id = []
      this.queryParams.time_range = [start, end]
      this.renderChart()
    },
    getChannels () {
      const list = this.permission_projectList.map(item => item.app_id)
      getChannelByAppId({ app_ids: list.join(',') }).then(res => {
        const channelList = res.data.list
        for (const item of this.permission_projectList) {
          item.label = item.name
          item.value = item.app_id + ''
          const list = channelList.filter(item1 => item1.app_id + '' === item.value)
          item.children = list.map(item => ({ label: `${item.name}(${item.channel_id})`, value: item.channel_id }))
        }
        this.channelOpts = this.permission_projectList
      })
    },
    getParameter (params = {}) {
      const query = {}
      for (const key in params) {
        let val = params[key]
        let flag = false
        if (key === 'time_range') {
          val = `${val[0] + 'T00:00:00'} : ${val[1] + 'T23:59:59'}`
        } else if (Array.isArray(val)) {
          // 选项中存在只有一个空串的情况，需要传递到后端
          flag = val.length === 1 && val[0] === ''
          val = val.join(',')
        }
        if (['status', 'coupon_id', 'channel_id'].includes(key) && (val !== '' || flag)) {
          let filters = query.filters || ''
          if (filters) filters += ' and '
          filters += `${key} in (${val})`
          query.filters = filters
        } else if (val !== '') {
          query[key] = val
        }
      }
      return query
    },
    getChartData (params = {}) {
      return getReportData(this.getParameter(params))
    },
    fmtNumer (val, label = '') {
      if (!/^(-?\d+)(\.\d+)?$/.test(val)) return '-'
      if (label === true || (typeof label === 'string' && label.includes('率'))) {
        val = val + '%'
      } else {
        val = numToText(val)
      }
      return val
    },
    // 格式化数字
    formatNumber (val, per, dec) {
      if (dec === undefined) dec = per ? 2 : 0
      return toFixed(val * (per ? 100 : 1), dec)
    },
    translateToMap (arr) {
      return arr.reduce((obj, t) => {
        const o = obj[t.__timestamp] = {}
        for (const k in t) {
          if (k !== '__timestamp') {
            o[k] = t[k] || 0
          }
        }
        return obj
      }, {})
    }
  },
  watch: {
    '$route.query.coupon_id': {
      handler (newVal, oldVal) {
        if (newVal && newVal !== oldVal) {
          this.init()
        }
      }
    }
  },
  computed: {
    ...mapGetters('zcsd-core', ['permission_projectList'])
  }
}
</script>

<style lang="scss" scoped>
.coupon-report {
  display: flex;
  padding: 20px;
  flex-direction: column;
  background-color: #eee;

  .card-wrap {
    display: flex;
    flex-wrap: wrap;
    margin-left: -10px;
    margin-right: -10px;

    .card-box {
      flex: 1;
      height: 190px;
      padding: 20px;
      min-width: 250px;
      max-width: 500px;
      margin-right: 10px;
      margin-left: 10px;
      margin-bottom: 20px;
      background-color: #fff;
      border: 1px solid rgba($color: #000, $alpha: 0.05);
      box-shadow: 2px 2px 4px 0px rgba($color: #000, $alpha: 0.3);
    }

    .card-top {
      display: flex;
      margin-bottom: 16px;
      justify-content: space-between;

      .title {
        font-size: 16px;
        cursor: pointer;

        &:hover {
          color: #1890ff;
        }
      }
    }

    .count {
      color: #000;
      font-size: 30px;
      padding-bottom: 4px;
      margin-bottom: 6px;
      border-bottom: 1px solid rgba($color: #000, $alpha: 0.1);
    }

    .more {
      line-height: 2;

      .num {
        margin-left: 30px;
      }
    }
  }

  .chart-wrap {
    flex: 1;
    display: flex;
    padding: 16px;
    overflow: hidden;
    flex-direction: column;
    background-color: #fff;

    .condition {
      display: flex;
      padding: 10px 0;
      align-items: center;
      justify-content: flex-end;

      .filters {
        display: inline-flex;

        .filter {
          margin-right: 20px;

          &:last-of-type {
            margin-right: 0;
          }

          .label {
            margin-right: 10px;
          }
        }
      }

      .btns {
        flex: 1;
        text-align: right;
      }
    }

    .content {
      flex: 1;
      overflow-y: auto;
    }

    .line-chart {
      .chart {
        margin: 14px 0;
        display: inline-block;
        width: calc(50% - 20px);
        height: 500px;

        &:nth-of-type(2n+1) {
          margin-right: 20px;
        }
      }
    }

    .area-chart {
      height: 500px;
      padding-top: 30px;
    }
  }
}
</style>
<style lang="scss">
.desc-tooltip {
  line-height: 1.5;
}

.area-chart .tooltip-wrap {
  .title {
    margin-bottom: 6px;
  }

  .series {
    .mark {
      display: inline-block;
      width: 10px;
      height: 10px;
      margin-right: 4px;
      border-radius: 50%;
    }

    .value {
      margin-left: 20px;
      font-weight: bold;
    }
  }
}
</style>
